/**
 * @ignore
 * undo,redo manager for kissy editor
 * @author yiminghe@gmail.com
 */

var Editor = require('editor');
var UA = require('ua'),
    LIMIT = 30;
var util = require('util');
/**
 * current editor status(including html and cursor position)
 * @param editor
 * @class KISSY.Editor.Undo.Snapshot
 * @private
 */
function Snapshot(editor) {
    var contents = editor.get('document')[0].body.innerHTML,
        self = this,
        selection;
    if (contents) {
        selection = editor.getSelection();
    }
    //内容html
    self.contents = contents;
    //选择区域书签标志
    self.bookmarks = selection && selection.createBookmarks2(true);
}

(Snapshot.prototype = {
    equals: function (otherImage) {
        var self = this,
            thisContents = self.contents,
            otherContents = otherImage.contents;
        // 不比较书签！
        // source mode -> wysiwyg mode 光标不保持
        return thisContents === otherContents;
    }
});

/**
 * manager history of editor content
 * @param editor
 * @class KISSY.Editor.UndoManager
 * @private
 */
function UndoManager(editor) {
    // redo undo history stack
    /**
     * 编辑器状态历史保存
     */
    var self = this;
    self.history = [];
    //当前所处状态对应的历史栈内下标
    self.index = -1;
    self.editor = editor;
    //键盘输入做延迟处理
    self.bufferRunner = util.buffer(self.save, 500, self);
    self._init();
}

var //editingKeyCodes = { /*Backspace*/ 8:1, /*Delete*/ 46:1 },
    modifierKeyCodes = { /*Shift*/ 16: 1, /*Ctrl*/ 17: 1, /*Alt*/ 18: 1 },
// Arrows: L, T, R, B
    navigationKeyCodes = { 37: 1, 38: 1, 39: 1, 40: 1, 33: 1, 34: 1 },
    zKeyCode = 90,
    yKeyCode = 89;

UndoManager.prototype = {
    _keyMonitor: function () {
        var self = this,
            editor = self.editor;

        editor.docReady(function () {
            editor.get('document').on('keydown', function (ev) {
                var keyCode = ev.keyCode;
                if (keyCode in navigationKeyCodes || keyCode in modifierKeyCodes) {
                    return;
                }
                // ctrl+z，撤销
                if (keyCode === zKeyCode && (ev.ctrlKey || ev.metaKey)) {
                    if (false !== editor.fire('beforeRedo')) {
                        self.restore(-1);
                    }
                    ev.halt();
                    return;
                }
                // ctrl+y，重做
                if (keyCode === yKeyCode && (ev.ctrlKey || ev.metaKey)) {
                    if (false !== editor.fire('beforeUndo')) {
                        self.restore(1);
                    }
                    ev.halt();
                    return;
                }
                if (editor.fire('beforeSave', {buffer: 1}) !== false) {
                    self.save(1);
                }
            });
        });
    },

    _init: function () {
        var self = this,
            editor = self.editor;
        self._keyMonitor();
        setTimeout(function () {
            // 只初始化保存一次，切换模式不保存
            if (editor.get('mode') === Editor.Mode.WYSIWYG_MODE) {
                if (editor.isDocReady()) {
                    self.save();
                } else {
                    editor.on('docReady', function docReady() {
                        self.save();
                        editor.detach('docReady', docReady);
                    });
                }
            }
        }, 0);
    },

    /**
     * save to history
     */
    save: function (buffer) {

        var editor = this.editor;

        // 代码模式下不和可视模式下混在一起
        if (editor.get('mode') !== Editor.Mode.WYSIWYG_MODE) {
            return;
        }

        if (!editor.get('document')) {
            return;
        }

        if (buffer) {
            this.bufferRunner();
            return;
        }

        var self = this,
            history = self.history,
            l = history.length,
            index = self.index;

        //前面的历史抛弃
        l = Math.min(l, index + 1);

        var last = history[l - 1],
            current = new Snapshot(editor);

        if (!last || !last.equals(current)) {
            history.length = l;
            if (l === LIMIT) {
                history.shift();
                l--;
            }
            history.push(current);
            self.index = index = l;
            editor.fire('afterSave', {history: history, index: index});
        }
    },

    /**
     * restore from history
     */
    restore: function (d) {

        // 代码模式下不和可视模式下混在一起
        if (this.editor.get('mode') !== Editor.Mode.WYSIWYG_MODE) {
            return undefined;
        }

        var self = this,
            history = self.history,
            editor = self.editor,
            editorDomBody = editor.get('document')[0].body,
            snapshot = history[self.index + d];

        if (snapshot) {
            editorDomBody.innerHTML = snapshot.contents;
            if (snapshot.bookmarks) {
                editor.getSelection().selectBookmarks(snapshot.bookmarks);
            } else if (UA.ie) {
                // IE BUG: If I don't set the selection to *somewhere* after setting
                // document contents, then IE would create an empty paragraph at the bottom
                // the next time the document is modified.
                var $range = editorDomBody.createTextRange();
                $range.collapse(true);
                $range.select();
            }
            var selection = editor.getSelection();
            // 将当前光标，选择区域滚动到可视区域
            if (selection) {
                selection.scrollIntoView();
            }
            self.index += d;
            editor.fire(d < 0 ? 'afterUndo' : 'afterRedo', {
                history: history,
                index: self.index
            });
            editor.notifySelectionChange();
        }

        return snapshot;
    }
};

module.exports = {
    init: function (editor) {
        if (!editor.hasCommand('save')) {
            var undoRedo = new UndoManager(editor);
            editor.addCommand('save', {
                exec: function (_, buffer) {
                    editor.focus();
                    undoRedo.save(buffer);
                }
            });
            editor.addCommand('undo', {
                exec: function () {
                    editor.focus();
                    undoRedo.restore(-1);
                }
            });
            editor.addCommand('redo', {
                exec: function () {
                    editor.focus();
                    undoRedo.restore(1);
                }
            });
        }
    }
};
